package com.homedo.common.email.util;

import com.homedo.common.email.bean.EmailInfo;
import com.sun.mail.util.BASE64DecoderStream;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.mail.*;
import javax.mail.internet.AddressException;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class FetchingEmailUtil {


    private String SAVE_ATTACHMENTS_PATH = "/home/uploads/daps-api";

    private static final Logger logger = LoggerFactory.getLogger(FetchingEmailUtil.class);


    public List<EmailInfo> fetchingAllEmailInfos(Store store, boolean closeFolder) throws Exception {
        List<EmailInfo> emailInfos = new ArrayList<EmailInfo>();

        // create the folder object and open it
        Folder emailFolder = store.getFolder("INBOX");
        emailFolder.open(Folder.READ_ONLY);

        // retrieve all messages from the folder in an array
        Message[] messages = emailFolder.getMessages();
        for (Message message : messages) {
            EmailInfo emailInfo = new EmailInfo();
            writePart(message, emailInfo);
            emailInfos.add(emailInfo);
        }
        if (closeFolder) {
            emailFolder.close(false);
        }
        return emailInfos;
    }


    public List<EmailInfo> fetchingLimitEmailInfos(Store store, boolean closeFolder, int limit) throws Exception {
        List<EmailInfo> emailInfos = new ArrayList<EmailInfo>();

        // create the folder object and open it
        Folder emailFolder = store.getFolder("INBOX");
        emailFolder.open(Folder.READ_ONLY);

        // retrieve all messages from the folder in an array
        if(emailFolder.getMessageCount()<=limit){
            limit = 1;
        } else {
            limit = emailFolder.getMessageCount() - limit;
        }

        Message[] messages = emailFolder.getMessages(limit, emailFolder.getMessageCount());
        // Message[] messages = emailFolder.getMessages(1, limit);
        for (Message message : messages) {
            EmailInfo emailInfo = new EmailInfo();
            writePart(message, emailInfo);
            emailInfos.add(emailInfo);
        }
        if (closeFolder) {
            emailFolder.close(false);
        }
        return emailInfos;
    }


    /**
     * 获取一份最新的邮件
     *
     * @return
     * @throws Exception
     */
    public EmailInfo fetchingLatestEmailFromStore(Store store, boolean closeFolder) throws Exception {
        EmailInfo emailInfo = new EmailInfo();

        // create the folder object and open it
        Folder emailFolder = store.getFolder("INBOX");
        emailFolder.open(Folder.READ_ONLY);

        // retrieve the latest messages from the folder in an array
        Message message = emailFolder.getMessage(emailFolder.getMessageCount());
        writePart(message, emailInfo);

        if (closeFolder) {
            emailFolder.close(false);
        }
        return emailInfo;
    }

    /**
     * 获取发信箱最新的邮件
     *
     * @return
     * @throws Exception
     */
    public EmailInfo fetchingLatestEmailFromSent(Store store, boolean closeFolder) throws Exception {
        EmailInfo emailInfo = new EmailInfo();

        // create the folder object and open it
        Folder emailFolder = store.getFolder("Sent");
        emailFolder.open(Folder.READ_ONLY);

        // retrieve the latest messages from the folder in an array
        Message message = emailFolder.getMessage(emailFolder.getMessageCount());
        writePart(message, emailInfo);

        if (closeFolder) {
            emailFolder.close(false);
        }
        return emailInfo;
    }


    /**
     * 获取发信箱最新的邮件
     *
     * @return
     * @throws Exception
     */
    public List<EmailInfo> fetchingLimitEmailFromSent(Store store, boolean closeFolder, int limit) throws Exception {
        List<EmailInfo> emailInfos = new ArrayList<EmailInfo>();
        // create the folder object and open it
        Folder emailFolder = store.getFolder("Sent");
        emailFolder.open(Folder.READ_ONLY);

        // retrieve the latest messages from the folder in an array
        if(emailFolder.getMessageCount()<=limit){
            limit = 1;
        } else {
            limit = emailFolder.getMessageCount() - limit;
        }

        Message[] messages = emailFolder.getMessages(limit, emailFolder.getMessageCount());
        for (Message message : messages) {
            EmailInfo emailInfo = new EmailInfo();
            writePart(message, emailInfo);
            emailInfos.add(emailInfo);
        }

        if (closeFolder) {
            emailFolder.close(false);
        }
        return emailInfos;
    }

    /*
     * This method checks for content-type based on which, it processes and
     * fetches the content of the message
     */
    private void writePart(Part p, EmailInfo emailInfo) throws Exception {
        if (p instanceof Message)
            // Call methos writeEnvelope
            writeEnvelope((Message) p, emailInfo);

        logger.info("CONTENT-TYPE: " + p.getContentType());


//        if (p.getContentType().contains("inquiry")) {
//            if (p.getContentType().split(";").length > 2) {
//                String inquiry = p.getContentType().split(";")[2];
//                String inquiryId = inquiry.split("=")[1];
//                if(StringUtils.isNotBlank(inquiryId))
//                emailInfo.setInquiryID(Long.valueOf(inquiryId));
//
//            }
//          //  System.out.println("inquiryId======="+emailInfo.getInquiryID());
//        }

        if (p.isMimeType("text/plain")) {
            // check if the content is plain text
            logger.info("This is plain text");
            // 设置文本内容的正文
            emailInfo.setContent(MimeUtility.decodeText(p.getContent().toString()));
        } else if (p.isMimeType("multipart/*")) {
            // check if the content has attachment
           // emailInfo.setContainsAttachments(true);
            logger.info("--------------包含附件-------------");
            Multipart mp = (Multipart) p.getContent();
            int count = mp.getCount();
            for (int i = 0; i < count; i++)
                writePart(mp.getBodyPart(i), emailInfo);
        } else if (p.isMimeType("message/rfc822")) {
            // check if the content is a nested message // 包含内嵌的内容
            logger.info("This is a Nested Message");
            writePart((Part) p.getContent(), emailInfo);
        } else if (p.isMimeType("image/jpeg")) {
            // check if the content is an inline image
            Object o = p.getContent();
            InputStream x = (InputStream) o;
            // Construct the required byte array
            logger.info("x.length = " + x.available());
            // 开启线程保存文件
            logger.info("FileName = " + p.getFileName().toString());

            File cacheDir = new File(SAVE_ATTACHMENTS_PATH + "/" +  MessageIdUtil.removeAngleBrackets(emailInfo.getMessageID()) + "/");//设置目录参数
            if (!cacheDir.exists()) {
                cacheDir.mkdirs();
            }
            cacheDir = new File(SAVE_ATTACHMENTS_PATH + "/" +  MessageIdUtil.removeAngleBrackets(emailInfo.getMessageID()) + "/" + p.getFileName().toString());
            if (!cacheDir.exists()) {
                FileOutputStream out = new FileOutputStream(SAVE_ATTACHMENTS_PATH +"/"+  MessageIdUtil.removeAngleBrackets(emailInfo.getMessageID()) + "/" + p.getFileName().toString());
                byte[] buffer = new byte[1024];
                int len = 0;
                while ((len = x.read(buffer)) != -1) {
                    out.write(buffer, 0, len);
                }
                out.flush();
                out.close();
            }
            // new SaveFileThread(x, p.getFileName().toString()).start();

        } else if (p.getContentType().contains("image/")) {
            logger.info("content type" + p.getContentType());
            File cacheDir = new File(SAVE_ATTACHMENTS_PATH + "/" +  MessageIdUtil.removeAngleBrackets(emailInfo.getMessageID()) + "/");//设置目录参数
            if (!cacheDir.exists()) {
                cacheDir.mkdirs();
            }
            cacheDir = new File(SAVE_ATTACHMENTS_PATH + "/" + MessageIdUtil.removeAngleBrackets(emailInfo.getMessageID())+ "/" + p.getFileName().toString());
            if (!cacheDir.exists()) {
                DataOutputStream output = new DataOutputStream(
                        new BufferedOutputStream(new FileOutputStream(cacheDir)));
                BASE64DecoderStream test = (BASE64DecoderStream) p
                        .getContent();
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = test.read(buffer)) != -1) {
                    output.write(buffer, 0, bytesRead);
                }
                output.flush();
                output.close();
            }

        } else {
            Object o = p.getContent();
            if (o instanceof String) {
                // 设置文本内容的正文
                emailInfo.setContent(MimeUtility.decodeText(p.getContent().toString()));
            } else if (o instanceof InputStream) {
                logger.info("--------------- 附件 InputStream ------------");
                logger.info("p.getContentType():\n" + p.getContentType());
                logger.info("-------------p.getContentType()--------------");

                String attachmentFileName = p.getDataHandler().getDataSource().getName();

                if (attachmentFileName != null) {
                    attachmentFileName = MimeUtility.decodeText(attachmentFileName);

                    InputStream fileIn = p.getDataHandler().getDataSource().getInputStream();
                    List<Map<String, String>> attachmentFiles = emailInfo.getAttachmentFiles();
                    Map map = new HashMap();
                    map.put("oldName", SAVE_ATTACHMENTS_PATH + "/" +  MessageIdUtil.removeAngleBrackets(emailInfo.getMessageID()) + "/" + attachmentFileName);
                    map.put("cid", getCid((Part) p));
                    attachmentFiles.add(map);

                    // 保存附件路径及名称
                    emailInfo.setAttachmentFiles(attachmentFiles);

                    if(emailInfo.getAttachmentFiles()!=null&&emailInfo.getAttachmentFiles().size()!=0){
                        emailInfo.setContainsAttachments(true);
                    }else {
                        emailInfo.setContainsAttachments(false);
                    }
                    // 开启线程保存文件
                    //   new SaveFileThread(fileIn, attachmentFileName).start();
                    File cacheDir = new File(SAVE_ATTACHMENTS_PATH + "/" + MessageIdUtil.removeAngleBrackets(emailInfo.getMessageID()) + "/");//设置目录参数

                    if (!cacheDir.exists()) {
                        cacheDir.mkdirs();
                    }
                    cacheDir = new File(SAVE_ATTACHMENTS_PATH + "/" +  MessageIdUtil.removeAngleBrackets(emailInfo.getMessageID()) + "/" + attachmentFileName);
                    if (!cacheDir.exists()) {
                        FileOutputStream out = new FileOutputStream(SAVE_ATTACHMENTS_PATH + "/" +  MessageIdUtil.removeAngleBrackets(emailInfo.getMessageID()) + "/" + attachmentFileName);
                        byte[] buffer = new byte[1024];
                        int len = 0;
                        while ((len = fileIn.read(buffer)) != -1) {
                            out.write(buffer, 0, len);
                        }
                        out.flush();
                        out.close();
                    }

                }

            } else {
                logger.info("This is an unknown type");
                logger.info("---------------------------");
                logger.info(o.toString());
            }
        }

    }

    public static String getCid(Part p) throws MessagingException {
        String cidraw = null, cid = null;
        String[] headers = p.getHeader("Content-id");
        if (headers != null && headers.length > 0) {
            cidraw = headers[0];
        } else {
            return null;
        }
        if (cidraw.startsWith("<") && cidraw.endsWith(">")) {
            cid = "cid:" + cidraw.substring(1, cidraw.length() - 1);
        } else {
            cid = "cid:" + cidraw;
        }
        return cid;

    }

    /*
     * 封装 EmailInfo
     */
    private static void writeEnvelope(Message m, EmailInfo emailInfo) throws Exception {
        Address[] a;
        // 设置发送时间
        emailInfo.setSentDate(m.getSentDate());
        // FROM
        if ((a = m.getFrom()) != null) {
            // 注意需要 decode
            logger.info("From address: " + MimeUtility.decodeText(a[0].toString()));
            emailInfo.setFromAddress(MimeUtility.decodeText(a[0].toString()));
        }

        // TO
        try {
            a = m.getRecipients(Message.RecipientType.TO);
        } catch (AddressException e) {
            logger.info("*********** TO Illegal semicolon *************");
            logger.info(e.getMessage());
        }
        if (a != null) {
            String[] toes = new String[a.length];
            for (int j = 0; j < a.length; j++) {
                logger.info("TO address: " + MimeUtility.decodeText(a[j].toString()));
                toes[j] = MimeUtility.decodeText(a[j].toString());
            }
            emailInfo.setToAddress(toes);
        }

        // CC
        try {
            a = m.getRecipients(Message.RecipientType.CC);
        } catch (Exception e) {
            logger.info("*********** CC Illegal semicolon *************");
            logger.info(e.getMessage());
        }
        if (a != null) {
            String[] toes = new String[a.length];
            for (int j = 0; j < a.length; j++) {
                logger.info("TO CC: " + MimeUtility.decodeText(a[j].toString()));
                toes[j] = MimeUtility.decodeText(a[j].toString());
            }
            emailInfo.setCarbonCopy(toes);
        }

        // BCC
        try {
            a = m.getRecipients(Message.RecipientType.BCC);
        } catch (Exception e) {
            logger.info("*********** BCC Illegal semicolon *************");
            logger.info(e.getMessage());
        }
        if (a != null) {
            String[] toes = new String[a.length];
            for (int j = 0; j < a.length; j++) {
                logger.info("TO BCC: " + MimeUtility.decodeText(a[j].toString()));
                toes[j] = MimeUtility.decodeText(a[j].toString());
            }
            emailInfo.setDarkCopy(toes);
        }

        // SUBJECT
        if (m.getSubject() != null) {
            logger.info("SUBJECT: " + MimeUtility.decodeText(m.getSubject()));
            emailInfo.setSubject(MimeUtility.decodeText(m.getSubject()));
        }

        //判断邮件是否已读
        boolean isNew = false;
        Flags flags = m.getFlags();
        Flags.Flag[] flag = flags.getSystemFlags();

        for (int i = 0; i < flag.length; i++) {
            if (flag[i] == Flags.Flag.SEEN) {
                isNew = true;
                logger.info("seen email...");
                // break;
            }
        }
        emailInfo.setReaded(isNew);
        /*
         * This message is seen. This flag is implicitly set by the
		 * implementation when the this Message's content is returned to the
		 * client in some form. The getInputStream and getContent methods on
		 * Message cause this flag to be set.
		 */
        //emailInfo.setReaded(false);

        // 判断是否需要回执
        boolean needReply = m.getHeader("Disposition-Notification-To") != null ? true : false;
        emailInfo.setNeedReply(needReply);

        if (m.getHeader("In-Reply-To") != null) {
            emailInfo.setReplyMessageID(MimeUtility.decodeText(MimeUtility.decodeText(StringUtils.join(m.getHeader("In-Reply-To")))));
        }

        // 获取该邮件的Message-ID
        String messageID = ((MimeMessage) m).getMessageID();

        emailInfo.setMessageID(messageID);
    }



    /**
     * 保存附件的线程
     *
     * @author taylor
     */

    private class SaveFileThread extends Thread {

        private String filename;
        private InputStream fileIn;


        public  SaveFileThread(InputStream fileIn, String filename) {
            this.filename = filename;
            this.fileIn = fileIn;
        }

        @Override
        public  void run() {
            FileOutputStream out = null;
            try {

                out = new FileOutputStream(SAVE_ATTACHMENTS_PATH + filename);
                byte[] buffer = new byte[1024];
                int len = 0;
                while ((len = fileIn.read(buffer)) != -1) {
                    out.write(buffer, 0, len);
                }
                out.flush();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (out != null) {
                        out.close();
                        out = null;
                    }
                    if (fileIn != null) {
                        fileIn.close();
                        fileIn = null;
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
